//
//  StreamPeer.m
//  YuchBox
//
//  Created by Ivan on 11-10-7.
//  Copyright 2011年 __MyCompanyName__. All rights reserved.
//

#import "StreamPeer.h"
#import "Utility.h"
@implementation StreamPeer

#pragma mark - static methods start here

+ (NSInteger)readBytesWithInStream:(NSInputStream *)inStream ptr:(UInt8 *)ptr maxLength:(NSInteger)len
{
    NSInteger unReadLen = len;
    UInt8 *ptrSeeker    = ptr;
    // use the block way 
#define MAX_TRYTIMES 1
    NSInteger tryTimes = 0;
    while (true) {
        if (unReadLen > 0) {
            NSInteger  readLen = [inStream read:ptrSeeker maxLength:unReadLen];
            
            if (readLen == -1 || readLen == 0) {
                if (tryTimes++ < MAX_TRYTIMES) {
                    sleep(1);
                } else {
                    TTLOG_ERROR(@"read inStream error!");
                    break;
                }
            } else {
                unReadLen -= readLen;
                ptrSeeker += readLen;
            }
            
        } else {
            break;
        }
    }
    
    // assert(unReadLen == 0);
    return len - unReadLen;
}

+ (UInt8)readInt8WithInstream:(NSInputStream *)inStream
{
    UInt8 value;
    int readLen = [StreamPeer readBytesWithInStream:inStream ptr:&value maxLength:sizeof(UInt8)];
    //[inStream read:&value maxLength:1];
    if (readLen == 0) {
        return -1;
    }
    return value;
}

+ (NSMutableData *)readNSDataInStream:(NSInputStream *)inStream maxLength:(NSInteger)length
{
    NSMutableData *data = [NSMutableData dataWithLength:length];
    [StreamPeer readBytesWithInStream:inStream ptr:(UInt8 *)[data mutableBytes] maxLength:length];

    return data;
}


+ (NSInteger)readInt32WithInStream:(NSInputStream *)inStream
{
    NSInteger value;
    [StreamPeer readBytesWithInStream:inStream ptr:(UInt8*)&value maxLength:sizeof(NSInteger)];
    //int readLen = [inStream read:(UInt8 *)&value maxLength:sizeof(NSInteger)];
    
    return value;
}

+ (UInt64)readInt64WithInStream:(NSInputStream *)inStream
{
    UInt64 value;
    [StreamPeer readBytesWithInStream:inStream ptr:(UInt8*)&value maxLength:sizeof(UInt64)];
    //[inStream read:(UInt8 *)&value maxLength:sizeof(UInt64)];
    
    return value;
}

+ (NSString *)readStringWithInStream:(NSInputStream *)inStream;
{
    // the format of message for string is 
    //         +-----------------------+
    // string  |len |string content... |
    //         +-----------------------+
    
    NSInteger stringLength = [StreamPeer readInt32WithInStream:inStream];
    UInt8 *stringBytes = (UInt8 *)malloc(stringLength + 1);

    // TODO how to reduce the memcpy times to 1 copy, 2 times here
    [StreamPeer readBytesWithInStream:inStream ptr:stringBytes maxLength:stringLength];
    //[inStream read:stringBytes maxLength:stringLength];
    stringBytes[stringLength] = '\0';
    
    //NSString *stringTemp = ; 
    return [NSString stringWithUTF8String:(const char*)stringBytes];
}

+ (void)readArrayWithInStream:(NSInputStream *)inStream outArray:(NSMutableArray *)array
{
    // the format of message for array is 
    //         +--------------------------+
    // string  |size |string|string|...   |
    //         +--------------------------+
    
    NSInteger arrayCount = [StreamPeer readInt32WithInStream:inStream];
    for (int i = 0 ; i < arrayCount ; i++) {
        [array addObject:[StreamPeer readStringWithInStream:inStream]];
    }
}

+ (void)writeBytesWithOutStream:(NSOutputStream *)outStream ptr:(UInt8 *)ptr maxLength:(NSInteger)length
{
    NSInteger unWriteLen = length;
    UInt8 *ptrSeeker     = ptr;
    while ([outStream hasSpaceAvailable]) {
        if (unWriteLen > 0) {
            NSInteger writeLen = [outStream write:ptrSeeker maxLength:unWriteLen];
            
            if (writeLen == -1) {
                TTLOG_ERROR(@"out stream write error!");
                CFStreamError error = CFWriteStreamGetError((CFWriteStreamRef)outStream);
                [StreamPeer printStreamError:error];
                break;
            } else if (writeLen == 0) {
                // stream write 0
                break;
            } else {
                unWriteLen -= writeLen;
                ptrSeeker  -= writeLen;
            }
        } else {
            break;
        }
    }
    
    assert(unWriteLen == 0);
}

+ (void)writeInt8WithOutStream:(NSOutputStream *)outStream int8Value:(UInt8)int8Value
{
    [StreamPeer writeBytesWithOutStream:outStream ptr:&int8Value maxLength:sizeof(UInt8)];
}

+ (void)writeInt32WithOutStream:(NSOutputStream *)outStream intValue:(NSInteger)intValue
{
    UInt8 *ptr = (UInt8 *)&intValue;
    [StreamPeer writeBytesWithOutStream:outStream ptr:ptr maxLength:sizeof(NSInteger)];
//    [outStream write:ptr maxLength:sizeof(NSInteger)];    
}

+ (void)writeInt64WithOutStream:(NSOutputStream *)outStream int64Value:(UInt64)int64Value
{
    UInt8 *ptr = (UInt8 *)&int64Value;
    [StreamPeer writeBytesWithOutStream:outStream ptr:ptr maxLength:sizeof(UInt64)];
}

+ (void)writeStringWithOutStream:(NSOutputStream *)outStream stringValue:(NSString *)stringValue
{
    UInt8 *ptr = (UInt8 *)[stringValue UTF8String];
    NSInteger strLen = strlen((const char*)ptr);
    
    [StreamPeer writeInt32WithOutStream:outStream intValue:strLen];
    [StreamPeer writeBytesWithOutStream:outStream ptr:ptr maxLength:strLen];
    //NSLog(@"%d == %d", strLen, [stringValue length]);
   // [outStream write:(UInt8 *)[stringValue UTF8String] maxLength:stringValue.length];
}

+ (void)writeArrayWithOutStream:(NSOutputStream *)outStream arrayValue:(NSMutableArray *)arrayValue
{
    [StreamPeer writeInt32WithOutStream:outStream intValue:arrayValue.count];

    for (NSString *stringValue in arrayValue) {
        [StreamPeer writeStringWithOutStream:outStream stringValue:stringValue];
    }
}

+ (void)printStreamError:(CFStreamError)error
{
    if (kCFStreamErrorDomainPOSIX == error.domain) {
        NSLog(@"[Error]POSIX error %s\n", strerror(error.error));
    } else if (kCFStreamErrorDomainMacOSStatus == error.domain) {
        NSLog(@"[Error]MacOs error, error no: %s\n", strerror(error.error));
    } else {
        NSLog(@"[Error]UNKOWN error, domain code:%@, error code: %s\n", error.domain, strerror(error.error)); 
    }
}
@end
